(function(angular) {

    function BwcUploadController($scope, $element) {

        //init default data
        function noop() {}
        $scope.$ctrl.headers = $scope.$ctrl.headers ? $scope.$ctrl.headers : {}
        $scope.$ctrl.name = $scope.$ctrl.name ? $scope.$ctrl.name : 'file'
        $scope.$ctrl.showFileList = $scope.$ctrl.showFileList ? $scope.$ctrl.showFileList : true
        $scope.$ctrl.type = $scope.$ctrl.type ? $scope.$ctrl.type : 'select'
        $scope.$ctrl.onRemove = $scope.$ctrl.onRemove ? $scope.$ctrl.onRemove : noop
        $scope.$ctrl.onChange = $scope.$ctrl.onChange ? $scope.$ctrl.onChange : noop
        $scope.$ctrl.onSuccess = $scope.$ctrl.onSuccess ? $scope.$ctrl.onSuccess : noop
        $scope.$ctrl.onProgress = $scope.$ctrl.onProgress ? $scope.$ctrl.onProgress : noop
        $scope.$ctrl.onError = $scope.$ctrl.onError ? $scope.$ctrl.onError : noop
        $scope.$ctrl.fileList = $scope.$ctrl.fileList ? $scope.$ctrl.fileList : []
        $scope.$ctrl.autoUpload = $scope.$ctrl.autoUpload ? $scope.$ctrl.autoUpload : true
        $scope.$ctrl.listType = $scope.$ctrl.listType ? $scope.$ctrl.listType : 'text'

        //init data end

        /**
         * data
         */
        $scope.uploadFiles = []
        $scope.dragOver = false
        $scope.draging = false
        $scope.tempIndex = 1

        if ($scope.$ctrl.fileList.length > 0) {
            for (var i = 0; i < $scope.$ctrl.fileList.length; i++) {
                $scope.uploadFiles.push({
                    uid: $scope.$ctrl.fileList[i].uid || (Date.now() + $scope.tempIndex++),
                    status: 'success',
                    name: $scope.$ctrl.fileList[i].name,
                    url: $scope.$ctrl.fileList[i].url
                })
            }
        }

        /**
         * method
         */
        $scope.handleProgress = function(ev, rawFile) {
            var file = $scope.getFile(rawFile);
            $scope.$ctrl.onProgress(ev, file, $scope.uploadFiles);
            file.status = 'uploading';
            file.percentage = ev.percent || 0;
        }
        $scope.handleSuccess = function(res, rawFile) {
            var file = $scope.getFile(rawFile);

            if (file) {
                file.status = 'success';
                file.response = res;
                $scope.$ctrl.onSuccess(res, file, $scope.uploadFiles);
                $scope.$ctrl.onChange(file, $scope.uploadFiles);
            }
        }
        $scope.handleError = function(err, rawFile) {
            var file = $scope.getFile(rawFile);
            var fileList = $scope.uploadFiles;

            file.status = 'fail';

            fileList.splice(fileList.indexOf(file), 1);

            $scope.$ctrl.onError(err, file, $scope.uploadFiles);
            $scope.$ctrl.onChange(file, $scope.uploadFiles);
        }
        $scope.handleRemove = function(file) {
            var fileList = $scope.uploadFiles;
            fileList.splice(fileList.indexOf(file), 1);
            $scope.$ctrl.onRemove(file, fileList);
        }
        $scope.getFile = function(rawFile) {
            var fileList = $scope.uploadFiles;
            var target;
            fileList.every(item => {
                target = rawFile.uid === item.uid ? item : null;
                return !target;
            });
            return target;
        }
        $scope.clearFiles = function() {
            this.uploadFiles = [];
        }
        $scope.submit = function() {
            this.uploadFiles
                .filter(file => file.status === 'ready')
                .forEach(file => {
                    this.$refs['upload-inner'].upload(file.raw, file);
                });
        }
        $scope.handleClick = function(e) {
            setTimeout(function() {
                angular.element(e.currentTarget).children().click()
            }, 1)
        }
        $scope.handleChange = function(ev) {
            const files = ev.files;
            if (!files) return;
            $scope.uploadFilesFn(files);
            angular.element(ev)[0].value = null
            $scope.file = null;
            $scope.$apply()
        }

        $scope.uploadFilesFn = function(files) {
            let postFiles = Array.prototype.slice.call(files);
            if (!$scope.$ctrl.multiple) { postFiles = postFiles.slice(0, 1); }

            if (postFiles.length === 0) { return; }

            postFiles.forEach(rawFile => {
                if (!$scope.$ctrl.thumbnailMode || $scope.isImage(rawFile.type)) {
                    $scope.onStart(rawFile);
                    if ($scope.$ctrl.autoUpload) $scope.upload(rawFile);
                }
            });
        }
        $scope.isImage = function(str) {
            return str.indexOf('image') !== -1;
        }
        $scope.onStart = function(rawFile) {
            rawFile.uid = Date.now() + this.tempIndex++;
            let file = {
                status: 'ready',
                name: rawFile.name,
                size: rawFile.size,
                percentage: 0,
                uid: rawFile.uid,
                raw: rawFile
            };

            try {
                file.url = URL.createObjectURL(rawFile);
            } catch (err) {
                console.error(err);
                return;
            }

            $scope.uploadFiles.push(file);
        }

        $scope.upload = function(rawFile, file) {
            if (!$scope.$ctrl.beforeUpload) {
                return post(rawFile);
            }

            const before = $scope.$ctrl.beforeUpload(rawFile);
            if (before && before.then) {
                before.then(processedFile => {
                    if (Object.prototype.toString.call(processedFile) === '[object File]') {
                        post(processedFile);
                    } else {
                        post(rawFile);
                    }
                }, () => {
                    $scope.$ctrl.onRemove(rawFile, true);
                });
            } else if (before !== false) {
                post(rawFile);
            } else {
                $scope.$ctrl.onRemove(rawFile, true);
            }
        }

        function post(rawFile) {
            ajax({
                headers: $scope.$ctrl.headers,
                withCredentials: $scope.$ctrl.withCredentials,
                file: rawFile,
                data: $scope.$ctrl.data,
                filename: $scope.$ctrl.name,
                action: $scope.$ctrl.action,
                onProgress: e => {
                    $scope.handleProgress(e, rawFile);
                    $scope.$apply();
                },
                onSuccess: res => {
                    $scope.handleSuccess(res, rawFile);
                    $scope.$apply();
                },
                onError: err => {
                    $scope.handleError(err, rawFile);
                    $scope.$apply();
                }
            });
        }

        function ajax(option) {
            if (typeof XMLHttpRequest === 'undefined') {
                return;
            }

            const xhr = new XMLHttpRequest();
            const action = option.action;

            if (xhr.upload) {
                xhr.upload.onprogress = function progress(e) {
                    if (e.total > 0) {
                        e.percent = e.loaded / e.total * 100;
                    }
                    option.onProgress(e);
                };
            }

            const formData = new FormData();

            if (option.data) {
                Object.keys(option.data).map(key => {
                    formData.append(key, option.data[key]);
                });
            }

            formData.append(option.filename, option.file);

            xhr.onerror = function error(e) {
                option.onError(e);
            };

            xhr.onload = function onload() {
                if (xhr.status < 200 || xhr.status >= 300) {
                    return option.onError(getError(action, option, xhr));
                }

                option.onSuccess(getBody(xhr));
            };

            xhr.open('post', action, true);

            if (option.withCredentials && 'withCredentials' in xhr) {
                xhr.withCredentials = true;
            }

            const headers = option.headers || {};

            for (let item in headers) {
                if (headers.hasOwnProperty(item) && headers[item] !== null) {
                    xhr.setRequestHeader(item, headers[item]);
                }
            }
            xhr.send(formData);
            return xhr;
        }

        function getError(action, option, xhr) {
            let msg;
            if (xhr.response) {
                msg = `${xhr.status} ${xhr.response.error || xhr.response}`;
            } else if (xhr.responseText) {
                msg = `${xhr.status} ${xhr.responseText}`;
            } else {
                msg = `fail to post ${action} ${xhr.status}'`;
            }

            const err = new Error(msg);
            err.status = xhr.status;
            err.method = 'post';
            err.url = action;
            return err;
        }

        function getBody(xhr) {
            const text = xhr.responseText || xhr.response;
            if (!text) {
                return text;
            }

            try {
                return JSON.parse(text);
            } catch (e) {
                return text;
            }
        }
    }

    angular.module('bwc.component')
        .component('bwcUpload', {
            template: `<div>
                            <span ng-if="$ctrl.listType === 'picture-card'">
                                <bwc-upload-list ng-if="$ctrl.showFileList" list-type='{{$ctrl.listType}}' files='uploadFiles' handle-remove='handleRemove' handle-preview='$ctrl.onPreview'>
                                </bwc-upload-list>
                            </span>
                            <div class="el-upload" ng-class="['el-upload--'+$ctrl.listType]" ng-click="handleClick($event)">
                                <div ng-if="$ctrl.drag">
                                    <bwc-upload-dragger on-file="uploadFilesFn"> <span ng-transclude></span></bwc-upload-dragger>
                                </div>
                                <span ng-if="!$ctrl.drag" ng-transclude></span>
                                <input class="el-upload__input" type="file" ng-model="file" onchange="angular.element(this).scope().handleChange(this)" multiple="{{$ctrl.multiple}}" accept="{{$ctrl.accept}}"></input>
                            </div>
                            <span ng-transclude="tip"></span>
                            <span ng-if="$ctrl.listType !== 'picture-card'">
                                <bwc-upload-list ng-if="$ctrl.showFileList" list-type='{{$ctrl.listType}}' files='uploadFiles' handle-remove='handleRemove' handle-preview='$ctrl.onPreview'>
                                </bwc-upload-list>
                            </span>
                        </div>
                        `,
            controller: ['$scope', BwcUploadController],
            transclude: {
                tip: "?tip"
            },
            replace: true,
            bindings: {
                action: "@",
                headers: "=?",
                data: "=?",
                multiple: "=?",
                name: "@?",
                drag: "<?",
                dragger: "=?",
                withCredentials: "=?",
                showFileList: "<?",
                accept: "@?",
                type: "@?",
                beforeUpload: "=?",
                onRemove: "=?",
                onChange: "=?",
                onPreview: "=?",
                onSuccess: "=?",
                onProgress: "=?",
                onError: "=?",
                fileList: "=?",
                autoUpload: "<?",
                listType: "@?",
            }
        });

})(angular);